package simmar.xcs;

import simmar.utilities.Logger;
import simmar.xcs.XCSAgent;
import ch.idsia.benchmark.mario.engine.GlobalOptions;
import ch.idsia.benchmark.mario.engine.sprites.Mario;
import ch.idsia.benchmark.tasks.SystemOfValues;
import ch.idsia.benchmark.tasks.Task;
import ch.idsia.tools.MarioAIOptions;

/**
 * 
 * @author Martin Dejean
 *
 */
public class XCSLearningTask extends XCSBasicTask implements Task
{
    private long _evaluations = 0;
    private int _gamesWon;
    private int _previousMarioMode;

    /**
     * @param marioAIOptions
     */
    public XCSLearningTask(MarioAIOptions marioAIOptions)
    {
        super(marioAIOptions);
        _gamesWon = 0;
        _previousMarioMode = Mario.STATUS_RUNNING;
    }

    /**
     * @return
     */
    public int evaluate(SystemOfValues sov)
    {
        environment.reset(options);
        Logger.logLine("Evaluation #" + _evaluations);
        
        this.runSingleEpisode(1);
        
        if (_evaluations++ > EVALUATION_QUOTA)
        {
            ((XCSAgent) options.getAgent()).printPopulation();
            ((XCSAgent) options.getAgent()).consolidateLearning();
            
            return 0;
        }   
        else
        {
            if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.SILENT) > 0)
            {
                Logger.log("Population is:\n");
                ((XCSAgent) options.getAgent()).printPopulation();                
            }
            
//            int marioStatus = evaluationInfo.marioStatus;
            
//            if (marioStatus == Mario.STATUS_WIN)
//                _gamesWon++;
            
//            Logger.logLine("Games won: " + _gamesWon + "/" + _evaluations);
            
//            float winRatio = 0.0f; 
//            
//            if (_evaluations != 0)
//            {
//                winRatio = _gamesWon / (float)_evaluations;
//            }
            
//            System.out.println(winRatio);

            int fitness = this.getEvaluationInfo().computeWeightedFitness(sov);
//            System.out.println(fitness);

            return fitness; 
        }            
    }
    
    public void runSingleEpisodeWithFitness(SystemOfValues sov)
    {
        this.runSingleEpisode(1);
        int fitness = this.getEvaluationInfo().computeWeightedFitness(sov);
//        int marioStatus = evaluationInfo.marioStatus;
        
//        if (marioStatus == Mario.STATUS_WIN)
//            _gamesWon++;
        
//        Logger.logLine("Games won: " + _gamesWon + "/" + _evaluations);
        
//        float winRatio = 0.0f; 
//        
//        if (_evaluations != 0)
//        {
//            winRatio = _gamesWon / (float)_evaluations;
//        }
//        
//        System.out.println(winRatio);

        
        System.out.println(fitness);
    }
    
    public void runSingleEpisode(final int repetitionsOfSingleEpisode)
    {
        for (int r = 0; r < repetitionsOfSingleEpisode; ++r)
        {
            this.reset();
            
            while (!environment.isLevelFinished())
            {
                environment.tick();
                
                if (!GlobalOptions.isGameplayStopped)
                {
                    if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.NORMAL) > 0)
                        Logger.logLine("------------------------------------------------------------- Task loop -------------------------------------------------------------");
                    
                    float reward = computeReward();
                    agent.integrateObservation(environment);
                    agent.giveIntermediateReward(reward);
                    boolean[] action = agent.getAction();
                    environment.performAction(action);
                }
            }
            
//            Logger.logLine("Level finished");
            
            environment.closeRecorder(); // recorder initialized in
                                         // environment.reset
            environment.getEvaluationInfo().setTaskName(name);
            this.evaluationInfo = environment.getEvaluationInfo().clone();
        }
    }

    public long getEvaluations()
    {
        return _evaluations;
    } 
    
    public float computeReward()
    {

//        _previousDistancePassed = _currentDistancePassed;
//        _currentDistancePassed = environment.getEvaluationInfo().computeDistancePassed();
////      Logger.logLine("Distance passed: " + _currentDistancePassed);
//        reward = (_currentDistancePassed - _previousDistancePassed);
        
        int[] previousCondition = agent.getPreviousCondition();
        Action previousAction = agent.getPreviousAction();
        
        if (agent.getPreviousCondition() == null)
            return 0.0f;
        
        if (XCS_DEBUG_LEVEL.compareTo(DEBUG_LEVEL.SILENT) > 0)
        {
            Logger.logLine("Previous condition was: " + agent.conditionToString(previousCondition));
            Logger.logLine("Previous action was: " + previousAction.toString()); 
        }
        
        float reward = 0.0f;
        
        int marioStatus = environment.getMarioStatus();
        int marioMode = environment.getMarioMode();
        
        _previousDistancePassed = _currentDistancePassed;
        _currentDistancePassed = environment.getEvaluationInfo().computeDistancePassed();
//        Logger.logLine("Distance passed: " + _currentDistancePassed);
        reward = (_currentDistancePassed - _previousDistancePassed);
        
        if (reward > 0)
            reward = 10;
        
        if (previousCondition[Condition.OBSTACLE_AHEAD.ordinal()] == 1 ||
            previousCondition[Condition.PIT_AHEAD.ordinal()] == 1)
        {
            if (previousCondition[Condition.CAN_ASCEND.ordinal()] == 1)
            {
                if (previousAction == Action.RIGHT_JUMP)
                {
                    reward = 100.0f;
                }
                else if (previousAction == Action.RIGHT_JUMP_SPEED)
                {
                    reward = 1000.0f;
                }
                else
                {
                    reward = 0.0f;
                }                    
            }
        }
        
        if (previousCondition[Condition.ENEMY_AHEAD.ordinal()] == 1)
        {
            if (previousCondition[Condition.CAN_ASCEND.ordinal()] == 1)
            {
                if (previousAction == Action.RIGHT_JUMP)
                {
                    reward = 100.0f;
                }
                else if (previousAction == Action.RIGHT_JUMP_SPEED)
                {
                    reward = 1000.0f;
                }
                else
                    reward = 0.0f;
            }
            else
            {
                if (previousAction == Action.RIGHT)
                {
                    reward = 0.0f;
                }
                else if (previousAction == Action.RIGHT_SPEED)
                {
                    if (previousCondition[Condition.SHOT_AIMED.ordinal()] == 1)
                    {
                        reward = 1000.0f;
                    }
                    else
                    {
                        reward = 0.0f;
                    }
                }                
            }            
        }
        
        if (marioMode < _previousMarioMode)
            reward = 0.0f;
        
        if (marioStatus == Mario.STATUS_DEAD)
            reward = 0.0f;
        
        _previousMarioMode = marioMode;
        
        return reward;
    }
}